#include "filesys/filesys.h"
#include <debug.h>
#include <stdio.h>
#include <string.h>
#include "filesys/file.h"
#include "filesys/free-map.h"
#include "filesys/inode.h"
#include "filesys/directory.h"
#include "threads/thread.h"
#include "lib/string.h"

/* Partition that contains the file system. */
struct block *fs_device;

static struct lock filesys_lock;

static void do_format (void);

//这个函数逐个截取source中的目录或者文件的名字
//截取到的文件名放在name中,name 为空代表根目录
//如果没有读到任何name，则返回false
//如果返回true但name为空，表示读取到了根目录，即开头的"/"
static bool file_path_cut(char **source,char *name)
{
    int i = 0;

    if (**source == '\0') return false;

    //如果source以/开头，则返回true并将name置为空
    if (**source == '/')
    {
        name[0] = '\0';
        while (**source == '/')
            (*source)++;

        return true;
    }

    while (**source != '/' && **source != '\0')
    {
        name[i] = **source;

        i++;
        if (i > MAX_FILE_NAME)
            return false;
        (*source)++;
    }
    name[i] = '\0';

    //去除多余的"/"
    while (**source == '/')
        (*source)++;

    return true;
}

/* Initializes the file system module.
   If FORMAT is true, reformats the file system. */
void
filesys_init (bool format) 
{
  fs_device = block_get_role (BLOCK_FILESYS);
  lock_init(&filesys_lock);
  if (fs_device == NULL)
    PANIC ("No file system device found, can't initialize file system.");

  inode_init ();
  free_map_init ();

  if (format) 
    do_format ();

  free_map_open ();
}

/* Shuts down the file system module, writing any unwritten data
   to disk. */
void
filesys_done (void) 
{
  free_map_close ();
}

/* Creates a file named NAME with the given INITIAL_SIZE.
   Returns true if successful, false otherwise.
   Fails if a file named NAME already exists,
   or if internal memory allocation fails. */
bool
filesys_create (const char *name, off_t initial_size) 
{
  block_sector_t inode_sector = 0;
  struct dir *dir;
  bool success = false;
  uint32_t depth;

  if (thread_current()->cur_dir == NULL)
  {
      thread_current()->cur_dir = dir_open_root();
      thread_current()->file_depth = 0;
  }

  depth = thread_current()->file_depth;
  dir = thread_current()->cur_dir;

  dir = dir_reopen(dir);

  if (dir != NULL
          && free_map_allocate (1, &inode_sector)
          && inode_create (inode_sector, initial_size))
  {
      char *path = name;
      char single_name[MAX_FILE_NAME + 1];

      while(file_path_cut(&path,single_name))
      {
          //限制目录的最大深度
          if (depth > MAX_DIR_DEPTH)
          {
              success = false;
              break;
          }
          if (single_name[0] == '\0')
              dir = dir_open_root();
          else
          {
              struct inode *inode = NULL;
              bool is_dir;
              if (dir_lookup(dir,single_name,&inode,&is_dir))
              {
                  if (is_dir)
                  {
                      dir_close(dir);
                      dir = dir_open(inode);
                      depth++;
                      if (dir == NULL)
                      {
                          success = false;
                          break;
                      }
                  }
                  else
                  {
                      success = false;
                      inode_close(inode);
                      break;
                  }
              }
              else
              {
                  if (path[0] != '\0')
                  {
                      success = false;
                      break;
                  }
                  else
                  {
                      lock_acquire(&filesys_lock);
                      //set_depth(inode_sector,get_depth(dir_get_inode(dir))+1);
                      success = dir_add(dir,single_name,inode_sector,false);
                      lock_release(&filesys_lock);
                  }
              }
          }
      }

  }

  if (!success && inode_sector != 0)
      free_map_release (inode_sector, 1);
  dir_close (dir);

  return success;
}

/* Opens the file with the given NAME.
   Returns the new file if successful or a null pointer
   otherwise.
   Fails if no file named NAME exists,
   or if an internal memory allocation fails. */
/*在原函数的基础上增加了一个参数，允许打开目录，如果打开的是目录,
  目录指针会被放在opened_dir中，同时返回NULL，如果两个都是NULL，
  则打开失败。*/
struct file *
filesys_open (const char *name, struct dir **opened_dir)
{
  struct dir *dir;
  struct inode *inode = NULL;
  char *path = name;
  char single_name[MAX_FILE_NAME + 1];
  bool is_dir;

  if (thread_current()->cur_dir == NULL)
  {
      thread_current()->cur_dir = dir_open_root();
      thread_current()->file_depth = 0;
  }

  dir = thread_current()->cur_dir;
  dir = dir_reopen(dir);

  while (file_path_cut(&path,single_name))
  {
      //printf ("single_name:%s\n",single_name);
      if (single_name[0] == '\0')
      {
          dir_close(dir);
          dir = dir_open_root();
      }
      else if (dir_lookup(dir,single_name,&inode,&is_dir))
      {
          if (is_dir)
          {
              dir_close(dir);
              dir = dir_open(inode);
              //printf ("dir:%p\n",dir);
              if (dir == NULL)
                  return false;
          }
          else
          {
              if (path[0] == '\0')
              {
                  dir_close(dir);
                  return file_open(inode);
              }
              //printf("isnotdir\n");
              break;
          }
      }
      else
      {
          //printf("notfound\n");
          return NULL;
      }
  }

  if (path[0] == '\0')
  {
      if (is_dir && opened_dir!=NULL)
      {
          *opened_dir = dir;
          return NULL;
      }
      else
      {
          dir_close(dir);
          return file_open(inode);
      }
  }
  else
      return false;
}

/* Deletes the file named NAME.
   Returns true if successful, false on failure.
   Fails if no file named NAME exists,
   or if an internal memory allocation fails. */
bool
filesys_remove (const char *name) 
{
  //struct dir *dir = dir_open_root ();
  struct dir *dir;
  bool success;
  char *path = name;
  char single_name[MAX_FILE_NAME + 1];
  struct inode *inode = NULL;
  bool is_dir;

  if (thread_current()->cur_dir == NULL)
  {
      thread_current()->cur_dir = dir_open_root();
      thread_current()->file_depth = 0;
  }


  if (!strcmp(name,"/") || !strcmp(name,"//"))
      return false;

  dir = thread_current()->cur_dir;
  dir = dir_reopen(dir);

  while (file_path_cut(&path,single_name))
  {
      if (single_name[0] == '\0')
      {
          dir_close(dir);
          dir = dir_open_root();
      }
      else if (dir_lookup(dir,single_name,&inode,&is_dir))
      {
          if (is_dir)
          {
              if (path[0] != '\0')
              {
                  dir_close(dir);
                  dir = dir_open(inode);
              }
              else
              {
                  inode_close(inode);
                  break;
              }
          }
          else
          {
              inode_close(inode);
              break;
          }
      }
      else
          break;
  }
  if (path[0] != '\0') return false;

  lock_acquire(&filesys_lock);
  success = dir_remove(dir,single_name);
  lock_release(&filesys_lock);

  dir_close (dir); 

  return success;
}

/* Formats the file system. */
static void
do_format (void)
{
  printf ("Formatting file system...");
  free_map_create ();
  if (!dir_create_root (MAX_FILE_CNT))
    PANIC ("root directory creation failed");
  free_map_close ();
  printf ("done.\n");
}


/*切换当前工作目录，如果请求的目录不存在，则返回false*/
bool filesys_chdir(const char *target_dir)
{
    struct dir *dir;
    char *path = target_dir;
    char single_name[MAX_FILE_NAME + 1];
    bool success = false;
    uint32_t olddepth;

    if (thread_current()->cur_dir == NULL)
    {
        thread_current()->cur_dir == dir_open_root();
        thread_current()->file_depth = 0;
    }

    olddepth = thread_current()->file_depth;
    dir = thread_current()->cur_dir;
    dir = dir_reopen(dir);

    while (file_path_cut(&path,single_name))
    {
        struct inode *inode = NULL;
        bool is_dir;
        if (dir_lookup(dir,single_name,&inode,&is_dir))
        {
            if (is_dir)
            {
                dir_close(dir);
                dir = dir_open(inode);
                olddepth++;
                success = true;
            }
            else
            {
                success = false;
                break;
            }
        }
        else
        {
            success = false;
            break;
        }
  }

    if (success)
    {
        dir_close(thread_current()->cur_dir);
        thread_current()->cur_dir = dir;
        thread_current()->file_depth = olddepth;
    }
    else
    {
        dir_close(dir);
    }

    return success;
}

/*创建一个新的目录，目录最大深度被限制位MAX_DIR_DEPTH*/
bool filesys_mkdir(const char *name)
{
    block_sector_t inode_sector = 0;
    struct dir *dir;
    bool success = false;
    uint32_t depth;

    if (thread_current()->cur_dir == NULL)
    {
        thread_current()->cur_dir = dir_open_root();
        thread_current()->file_depth = 0;
    }

    depth = thread_current()->file_depth;
    dir = thread_current()->cur_dir;

    dir = dir_reopen(dir);

    if (dir != NULL
            && free_map_allocate (1, &inode_sector))
    {
        char *path = name;
        char single_name[MAX_FILE_NAME + 1];

        while(file_path_cut(&path,single_name))
        {
            //限制目录的最大深度
            if (depth > MAX_DIR_DEPTH)
            {
                success = false;
                break;
            }
            if (single_name[0] == '\0')
                dir = dir_open_root();
            else
            {
                struct inode *inode = NULL;
                bool is_dir;
                if (dir_lookup(dir,single_name,&inode,&is_dir))
                {
                    if (is_dir)
                    {
                        dir_close(dir);
                        dir = dir_open(inode);
                        depth++;
                    }
                    else
                    {
                        success = false;
                        inode_close(inode);
                        break;
                    }
                }
                else
                {
                    if (path[0] != '\0')
                    {
                        success = false;
                        break;
                    }
                    else
                    {
                        lock_acquire(&filesys_lock);
                        dir_create(inode_sector,MAX_FILE_CNT);
                        //set_depth(inode_sector,get_depth(dir_get_inode(dir))+1);
                        success = dir_add(dir,single_name,inode_sector,true);
                        lock_release(&filesys_lock);
                    }
                }
            }
//            if (get_depth(dir_get_inode(dir)) > MAX_DIR_DEPTH)
//            {
//                success = false;
//                break;
//            }
        }
    }

    if (!success && inode_sector != 0)
        free_map_release (inode_sector, 1);
    dir_close (dir);

    return success;
}

